/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2008 Apple Inc.  All Rights Reserved.
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */

//
// QTAtom_dref:
//   The 'dref' QTAtom class.


// -------------------------------------
// Includes
//
#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include <string.h>

#ifndef __Win32__
#include <sys/types.h>
#include <netinet/in.h>
#endif

#include "QTFile.h"

#include "QTAtom.h"
#include "QTAtom_dref.h"
#include "OSMemory.h"


// -------------------------------------
// Macros
//
#define DEBUG_PRINT(s) if(fDebug) qtss_printf s
#define DEEP_DEBUG_PRINT(s) if(fDeepDebug) qtss_printf s



// -------------------------------------
// Constants
//
const int       drefPos_VersionFlags        =  0;
const int       drefPos_NumRefs             =  4;
const int       drefPos_RefTable            =  8;

const int       drefRefPos_Size             =  0;
const int       drefRefPos_Type             =  4;
const int       drefRefPos_VersionFlags     =  8;
const int       drefRefPos_Data             = 12;



// -------------------------------------
// Mac alias constants and typedefs
//

enum {
        kEndMark    =-1,            /* -1 end of variable info */
        kAbsPath    = 2,            /* 2  absolute path name */
        kMaxMark    = 10            /* End Marker */
};

typedef struct
{
    short           what;           /* what kind of information */
    short           len;            /* length of variable data */
    char            data[1];        /* actual data */
} varInfo;

typedef struct
{
    char            private1[130];
    short           nlvlFrom;       /* # of levels from fromFile/toFile until a common */
    short           nlvlTo;         /* ancestor directory is found */
    char            private2[16];
    varInfo         vdata[1];       /* variable length info */
} AliasRecordPriv;


// -------------------------------------
// Constructors and destructors
//
QTAtom_dref::QTAtom_dref(QTFile * File, QTFile::AtomTOCEntry * TOCEntry, Bool16 Debug, Bool16 DeepDebug)
    : QTAtom(File, TOCEntry, Debug, DeepDebug),
      fNumRefs(0), fRefs(NULL)
{
}

QTAtom_dref::~QTAtom_dref(void)
{
    //+ 6.16.1999 rt fix memory leak
    for( UInt32 curRef = 0; curRef < fNumRefs; curRef++ )
    {
        delete [] fRefs[curRef].Data;
        delete fRefs[curRef].FCB;
    }
    //-end
    
    //
    // Free our variables.
    delete[] fRefs;
}



// -------------------------------------
// Initialization functions
//
Bool16 QTAtom_dref::Initialize(void)
{
    // Temporary vars
    UInt32      tempInt32;

    // General vars
    UInt64      refPos;


    //
    // Parse this atom's fields.
    ReadInt32(drefPos_VersionFlags, &tempInt32);
    fVersion = (UInt8)((tempInt32 >> 24) & 0x000000ff);
    fFlags = tempInt32 & 0x00ffffff;

    ReadInt32(drefPos_NumRefs, &fNumRefs);

    //
    // Read in all of the refs.
    if( fNumRefs > 0 ) {
        //
        // Allocate our ref table.
        fRefs = NEW DataRefEntry[fNumRefs];
        if( fRefs == NULL )
            return false;

        //
        // Read them all in..
        refPos = drefPos_RefTable;
        for( UInt32 CurRef = 0; CurRef < fNumRefs; CurRef++ ) {
            //
            // Set up the entry.
            fRefs[CurRef].Flags = 0x0;
            fRefs[CurRef].ReferenceType = FOUR_CHARS_TO_INT('N', 'U', 'L', 'L'); // NULL
            fRefs[CurRef].DataLength = 0;
            fRefs[CurRef].Data = NULL;

            fRefs[CurRef].IsEntryInitialized = false;
            fRefs[CurRef].IsFileOpen = false;
            fRefs[CurRef].FCB = NULL;


            //
            // Get the flags and type.
            ReadInt32(refPos + drefRefPos_VersionFlags, &tempInt32);
            fRefs[CurRef].Flags = tempInt32 & 0x00ffffff;

            ReadInt32(refPos + drefRefPos_Type, &tempInt32);
            fRefs[CurRef].ReferenceType = tempInt32;
            
            //
            // We're done if this is a self-referencing atom.
            if( fRefs[CurRef].Flags & flagSelfRef ) {
                fRefs[CurRef].IsEntryInitialized = true;
                continue;
            }
            

            //
            // Get all of the data.
            ReadInt32(refPos + drefRefPos_Size, &tempInt32);
            fRefs[CurRef].DataLength = tempInt32 - 12 /* skip the header */;
            
            fRefs[CurRef].Data = NEW char[fRefs[CurRef].DataLength];
            if( fRefs[CurRef].Data == NULL ) {
                //
                // De-initialize this entry.
                fRefs[CurRef].DataLength = 0;
                fRefs[CurRef].IsEntryInitialized = false;
            } else {
                //
                // Read the entry data.
                ReadBytes(refPos + drefRefPos_Data, fRefs[CurRef].Data, fRefs[CurRef].DataLength);
                
                //
                // Configure the rest of the entry.
                fRefs[CurRef].FCB = NEW QTFile_FileControlBlock();
                if( fRefs[CurRef].FCB == NULL )
                    fRefs[CurRef].IsEntryInitialized = false;
                else
                    fRefs[CurRef].IsEntryInitialized = true;
                fRefs[CurRef].IsFileOpen = false;
            }

            //
            // Skip over this mini-atom.
            refPos += fRefs[CurRef].DataLength + 12 /* account for the header */;
        }
    }

    //
    // This atom has been successfully read in.
    return true;
}



// -------------------------------------
// Read functions.
//
Bool16 QTAtom_dref::Read(UInt32 RefID, UInt64 Offset, char * const Buffer, UInt32 Length, QTFile_FileControlBlock * FCB)
{
    // General vars
    DataRefEntry        *Entry;
    
    
    //
    // Validate that this ref exists.
    if( (RefID == 0) || (RefID > fNumRefs) )
        return false;

//  qtss_printf("QTAtom_dref::Read Offset = %qd, Length = %" _S32BITARG_ " \n", Offset, Length);
    //
    // If this data reference is in the movie file itself, then we can forward
    // the request directly to QTFile.
    if( IsRefInThisFile(RefID) )
        return fFile->Read(Offset, Buffer, Length, FCB);
    

    //
    // The ref is not in this file; see if we have already opened an FCB for
    // the file and use that, otherwise we need to process the ref.
    Entry = &fRefs[RefID - 1];
    
    //
    // Abort if the entry was not initialized.
    if( !Entry->IsEntryInitialized )
        return false;
    
    //
    // Open the file (after parsing the data reference) if it is not open
    // already.
    if( !Entry->IsFileOpen ) {
        // General vars
        char        *AliasPath;
        
        
        //
        // We only support parsing alias types.
        if( Entry->ReferenceType != FOUR_CHARS_TO_INT('a', 'l', 'i', 's') ) //alis
            return false;
        
        //
        // Parse the alias.
        if( (AliasPath = ResolveAlias(Entry->Data, Entry->DataLength)) == NULL )
            return false;
        
        //
        // Create a path which does *not* contain the current movie's name.
        char * p;
        char * MoviePath = fFile->GetMoviePath();
        char * NewPath = NEW char[strlen(MoviePath) + strlen(AliasPath) + 1];
        //char * NewPath = (char *)calloc(1, strlen(MoviePath) + strlen(AliasPath));
        if( (p = strrchr(MoviePath, QT_PATH_SEPARATOR)) == NULL ) {
            memcpy(NewPath, AliasPath, strlen(AliasPath) + 1);
        } else {
            int MoviePathClippedLength = (p - MoviePath) + 1;
            memcpy(NewPath, MoviePath, MoviePathClippedLength);
            memcpy(NewPath + MoviePathClippedLength, AliasPath, strlen(AliasPath) + 1);
        }

            
        //
        // Do the open.
        Entry->FCB->Set(NewPath);
        if(!Entry->FCB->IsValid()) {
            delete [] NewPath;
            //free(NewPath);
            return false;
        }
        
        Entry->IsFileOpen = true;
        delete [] NewPath;
        //free(NewPath);
    }
    
    //
    // Do the read.
    return fFile->Read(Offset, Buffer, Length, Entry->FCB);
}



// -------------------------------------
// Debugging functions
//
void QTAtom_dref::DumpAtom(void)
{
    DEBUG_PRINT(("QTAtom_dref::DumpAtom - Dumping atom.\n"));
    for( UInt32 CurRef = 1; CurRef <= fNumRefs; CurRef++ )
        DEBUG_PRINT(("QTAtom_dref::DumpAtom - ..Ref #%"   _U32BITARG_   " is in file: %s\n", CurRef, IsRefInThisFile(CurRef) ? "yes" : "no"));
}



// -------------------------------------
// Protected member functions.
//
char * QTAtom_dref::ResolveAlias(char * const AliasData, UInt32 /* AliasDataLength */)
{
    // General vars
    AliasRecordPriv     *Alias = (AliasRecordPriv *)AliasData;
    varInfo             *AliasVarInfo;
    
    char                *path, *pathStart;
    int                 pathLength;
    
    
    //
    // Verify that we have a relative path.
    if( (Alias->nlvlTo == -1) || (Alias->nlvlFrom == -1) )
        return NULL;
    
    //
    // Search for the absolute pathname in this alias.
    AliasVarInfo = Alias->vdata;
    for( int loopCount = kMaxMark - 1; loopCount >= 0; loopCount--) {
        //
        // Break out of the loop if this is a match/the end of the alias.
        if( ((short)ntohs(AliasVarInfo->what) == kAbsPath) || ((short)ntohs(AliasVarInfo->what) == kEndMark) )
            break;
        
        //
        // Otherwise we need to move to the next data unit.
        AliasVarInfo = (varInfo *)((char *)AliasVarInfo + ((ntohs(AliasVarInfo->len) + 1) & ~1) + 4 /* header size */);
    }


    //
    // Now that we have the path, we need to strip off the absolute portions
    // of it so that we can get at it from our current (relative) root.
    AliasVarInfo->data[ntohs(AliasVarInfo->len)] = '\0';
    
    pathStart = path = AliasVarInfo->data;
    path += ntohs(AliasVarInfo->len);
    int i = ntohs(Alias->nlvlTo);
    pathLength = -1;
    while( i && (path > pathStart) ) {
        if( *path-- == ':' )
            i--;
        pathLength++;
    }
    
    if( i == 1 )
        pathLength += 2;    // fell out of loop; we're relative to root
    else
        path += 2;          // points past separator character
    
    
    //
    // Return the alias.
    return path;
}
